#pragma once

#include "ConsoleIO.h"
#include <cstdint>
#include <string>

enum class OutputFileNameType
{
	Real_FFT,
	Imag_FFT,
	CSV_Complex,
	CSV_Real,
	BINARY_Complex,
	BINARY_Real
};

enum class FourierTransformedDataLayout
{
	PackedComplex_FftwFormat,
	PackedComplex_IppFormat,
	Complex,
	ComplexZeroFrequencyCentered
};

enum class OutputFormat
{
	CSV,
	BINARY,
	REALCOMPLEX_WDP_FLOAT,
	REALCOMPLEX_PNG_GRAY16NORMALIZED,
	REALCOMPLEX_PNG_GRAY8NORMALIZED
};

enum class FFTEngine
{
	CuFFT,
	IppFFT,
	FFTWFFT,
	D3DCSX,
	CLFFT
};

enum class TransformationType
{
	R2C,
	C2C_FWD,
	C2C_INV,
	C2R
};

enum class Command
{
	Transform,
	ListVersions,
	PassThroughSource
};

class CCmdLineOptions
{
private:
	uint32_t benchmarkRepeatCount;
	std::wstring sourceFilename;
	std::wstring outputPath;
	std::wstring outputFileName;
	FFTEngine fftEngine;
	TransformationType transformationType;
	FourierTransformedDataLayout ftDataLayout;
	OutputFormat outputFileFmt;
	double normalizationFactor;
	Command command;

	uint32_t randSrcWidth;
	uint32_t randSrcHeight;
	float randomGenMin, randomGenMax;
	uint32_t randomSeed;
	std::vector<std::wstring> additionalDllSearchPath;
public:
	CCmdLineOptions();
	bool Parse(int argc, wchar_t** argv, ILog* log);

	/// <summary>	Get a value indicating whether a benchmark operation should be done. </summary>
	/// <returns>	True if a benchmark operation shall be done, false otherwise. </returns>
	bool DoBenchmark() const;

	/// <summary>	Gets benchmark repeat count - only valid if a benchmark operation is enabled. </summary>
	/// <returns>	The benchmark repeat count. </returns>
	uint32_t GetBenchmarkRepeatCount() const;

	Command GetCommand() const;
	FFTEngine GetFFTEngine() const;
	FourierTransformedDataLayout GetFourierTransformDataLayout() const;
	OutputFormat GetOutputFileFormat() const;
	TransformationType GetTransformationType() const;

	/// <summary>	
	/// Gets normalization factor. 
	/// Negative infinity means "divide by size = width*height.
	/// Positive infinity means "divide by sqrt(size= sqrt(width*height).
	/// </summary>
	/// <returns>	The normalization factor. </returns>
	double GetNormalizationFactor() const;

	const std::wstring& GetSourceFilename() const;

	bool GetRandomSrcParameters(uint32_t* pWidth, uint32_t* pHeight, float* pRandMin, float* pRandMax, uint32_t* pRandSeed) const;

	const std::vector<std::wstring>& GetAdditionalDllSearchPath() const;

	std::wstring GenerateOutputFilename(OutputFileNameType type, const wchar_t* pszExtension) const;
	void GetDefaultFileExtension(OutputFormat outputFmt, std::wstring& str) const;

	void PrintUsage(ILog* log);

private:
	void SetDefaults();
	void AddDllSearchPath(const wchar_t* path);
	static std::tuple<int, int, float, float, uint32_t> ParseRandomSource(const wchar_t* s);
	static std::tuple<int, int, float, float, uint32_t> ParseRandomSource(const std::string& s);
	static std::wstring PathFromFullyQualifiedFilename(const std::wstring& filename);
	static std::wstring FilenameFromFullyQualifiedFilename(const std::wstring& filename);
	static void SplitFilename(const std::wstring& filename, std::wstring* fileNameWoExtension, std::wstring* extension);
	static std::wstring ConcatPath(const std::wstring& a, const std::wstring& b);
	static FFTEngine FFTEngineStringToEnum(const wchar_t* s);
	static FourierTransformedDataLayout DataLayoutStringToEnum(const wchar_t* s);
	static OutputFormat OutputFileFmtStringToEnum(const wchar_t* s);
	static TransformationType TransformationTypeStringToEnum(const wchar_t* s);
	static double ParseNormalizationFactor(const wchar_t* s);
	static double ParseNormalizationFactor(const std::wstring& s);
	static std::uint32_t ParseBenchmarkRepeatCount(const wchar_t* s);
	static Command ParseCommand(const wchar_t* s);
	static std::wstring GetExeName();
};